/**
 * 
 */
package com.googlecode.pcapj;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;

/**
 * This class is not thread safe.
 */
public class Copier {
	private static final int DEFAULT_BUFFER_SIZE = 4096;
	
	private static abstract class StreamRetrieveｒ{
		abstract InputStream getInputStream() throws IOException;
		abstract OutputStream getOutputStream() throws IOException;
	}

	private static abstract class ObjectStreamRetrieveｒ<T> extends StreamRetrieveｒ{
		private T object;
		
		private ObjectStreamRetrieveｒ(T object) {
			super();
			this.object = object;
		}
		@Override
		InputStream getInputStream() throws IOException{
			return getInputStream(object);
		}
		@Override
		OutputStream getOutputStream() throws IOException{
			return getOutputStream(object);
		}
		abstract InputStream getInputStream(T object) throws IOException;
		abstract OutputStream getOutputStream(T object) throws IOException;
	}
	
	private static class FileStreamRetrieveｒ extends ObjectStreamRetrieveｒ<File>{
		private FileStreamRetrieveｒ(File file) {
			super(file);
		}
		@Override
		InputStream getInputStream(File object) throws FileNotFoundException {
			return new FileInputStream(object);
		}
		@Override
		OutputStream getOutputStream(File object) throws FileNotFoundException {
			return new FileOutputStream(object);
		}
	}

	private static class URLStreamRetrieveｒ extends ObjectStreamRetrieveｒ<URL>{
		private URLStreamRetrieveｒ(URL url) {
			super(url);
		}
		private URLStreamRetrieveｒ(ClassLoader classLoader, String resourceName) {
			this(classLoader == null ? Copier.class.getClassLoader().getResource(resourceName) : classLoader.getResource(resourceName));
		}
		private URLStreamRetrieveｒ(ClassLoader classLoader, Package resourcePath, String resourceName) {
			this(
					classLoader,
					resourcePath.getName().replace('.', '/') + '/' + resourceName
					);
		}
		@Override
		InputStream getInputStream(URL object) throws IOException {
			return object.openStream();
		}
		@Override
		OutputStream getOutputStream(URL object) throws FileNotFoundException {
			throw new UnsupportedOperationException(this.getClass().getSimpleName() + ".getOutputStream(URL)");
		}
	}
	
	private byte[] bytes;
	
	/**
	 * 
	 */
	public Copier() {
		this(DEFAULT_BUFFER_SIZE);
	}
	
	public Copier(int bufferSize) {
		super();
		bytes = new byte[bufferSize];
	}
	private int copy(StreamRetrieveｒ source, StreamRetrieveｒ destination) throws IOException{
		InputStream inputStream = source.getInputStream();
		try{
			OutputStream outputStream = destination.getOutputStream();
			try{
				return copy(inputStream, outputStream);
				
			}finally{
				outputStream.close();
			}
			
		}finally{
			inputStream.close();
		}
	}
	public int copy(ClassLoader classLoader, Package srcPath, String srcName, File destination) throws IOException{
		return copy(new URLStreamRetrieveｒ(classLoader, srcPath, srcName), new FileStreamRetrieveｒ(destination));		
	}
	public int copy(ClassLoader classLoader, String resourceName, File destination) throws IOException{
		 return copy(new URLStreamRetrieveｒ(classLoader, resourceName), new FileStreamRetrieveｒ(destination));
	}
	public int copy(InputStream source, OutputStream destination) throws IOException{
		int copied = 0;
		for (int length = source.read(bytes); length != -1; length = source.read(bytes)) {
			if(length > 0){
				destination.write(bytes, 0, length);
				copied += length; 
			}
		}
		return copied;
	}
}
